const express = require('express')
const router = express.Router()
const pool = require('../db')
const authMiddleware = require('../middleware/auth')
const { createLog } = require('../log')

// GET /api/roles
// returns { roles: [ { id, name }, ... ], total }
router.get('/roles', authMiddleware, async (req, res) => {
  try {
    // log page open
    createLog(req, { message: '打开角色列表', tp: 'view' })

    const page = Math.max(1, parseInt(req.query.page, 10) || 1)
    const limit = Math.max(1, parseInt(req.query.limit, 10) || 10)
    const offset = (page - 1) * limit

    // allow specific sort fields to avoid SQL injection
    const allowedSorts = {
      id: 'id',
      role_name: 'role_name',
      role_code: 'role_code',
      role_level: 'role_level',
      create_time: 'create_time',
      update_time: 'update_time'
    }

    const sortBy = typeof req.query.sort_by === 'string' ? req.query.sort_by : null
    let sortOrder = typeof req.query.sort_order === 'string' ? req.query.sort_order.toLowerCase() : null
    if (sortOrder !== 'asc' && sortOrder !== 'desc') sortOrder = null

    let orderClause = 'create_time DESC'
    if (sortBy && allowedSorts[sortBy]) {
      orderClause = `${allowedSorts[sortBy]} ${sortOrder === 'asc' ? 'ASC' : 'DESC'}`
    }

    const [rows] = await pool.query(
      `SELECT id, role_name, role_code, role_level, role_desc, create_time, update_time FROM atb_role ORDER BY ${orderClause} LIMIT ? OFFSET ?`,
      [limit, offset]
    )

    const [countRows] = await pool.query('SELECT COUNT(*) AS total FROM atb_role')
    const total = countRows && countRows[0] && countRows[0].total ? countRows[0].total : 0

    return res.json({ roles: rows, total })
  } catch (err) {
    console.error('Failed to fetch roles:', err)
    // return empty roles to allow frontend to fallback
    return res.json({ roles: [], total: 0 })
  }
})

// POST /api/roles
// body: { role_name, role_code, role_level, role_desc }
router.post('/roles', authMiddleware, async (req, res) => {
  try {
    const { role_name, role_code, role_level, role_desc } = req.body
    if (!role_name) return res.status(400).json({ message: 'role_name required' })

    // check uniqueness
    const [rows] = await pool.query('SELECT id FROM atb_role WHERE role_name = ?', [role_name])
    if (rows && rows.length > 0) return res.status(409).json({ message: '角色已存在', field: 'role_name' })

    const [result] = await pool.query(
      'INSERT INTO atb_role (role_name, role_code, role_level, role_desc, create_time, update_time) VALUES (?, ?, ?, ?, NOW(), NOW())',
      [role_name, role_code || null, typeof role_level !== 'undefined' ? role_level : null, role_desc || null]
    )
    const insertId = result.insertId
    const [newRows] = await pool.query('SELECT id, role_name, role_code, role_level, role_desc, create_time, update_time FROM atb_role WHERE id = ?', [insertId])

    // log creation
    createLog(req, { message: `新增角色 ${role_name}`, tp: 'create', obj_json: JSON.stringify(newRows[0] || { id: insertId, role_name }) })

    res.status(201).json({ role: newRows[0] })
  } catch (err) {
    console.error('Failed to create role:', err)
    res.status(500).json({ message: 'Server error' })
  }
})

// PUT /api/roles/:id
// body: { role_name, role_code, role_level, role_desc }
router.put('/roles/:id', authMiddleware, async (req, res) => {
  try {
    const id = parseInt(req.params.id, 10)
    if (isNaN(id)) return res.status(400).json({ message: 'Invalid role id' })

    const { role_name, role_code, role_level, role_desc } = req.body
    if (!role_name) return res.status(400).json({ message: 'role_name required' })

    // check uniqueness excluding current id
    const [rows] = await pool.query('SELECT id FROM atb_role WHERE role_name = ? AND id != ?', [role_name, id])
    if (rows && rows.length > 0) return res.status(409).json({ message: '角色已存在', field: 'role_name' })

    // fetch existing row to preserve unspecified fields
    const [existingRows] = await pool.query('SELECT role_code, role_level, role_desc FROM atb_role WHERE id = ?', [id])
    if (!existingRows || existingRows.length === 0) return res.status(404).json({ message: 'Role not found' })
    const existing = existingRows[0]

    const finalRoleCode = typeof role_code !== 'undefined' ? role_code : existing.role_code
    const finalRoleLevel = typeof role_level !== 'undefined' ? role_level : existing.role_level
    const finalRoleDesc = typeof role_desc !== 'undefined' ? role_desc : existing.role_desc

    await pool.query(
      'UPDATE atb_role SET role_name = ?, role_code = ?, role_level = ?, role_desc = ?, update_time = NOW() WHERE id = ?',
      [role_name, finalRoleCode, finalRoleLevel, finalRoleDesc, id]
    )
    const [updatedRows] = await pool.query('SELECT id, role_name, role_code, role_level, role_desc, create_time, update_time FROM atb_role WHERE id = ?', [id])

    // log update
    createLog(req, { message: `编辑角色 id=${id}`, tp: 'update', obj_json: JSON.stringify(updatedRows[0] || { id }) })

    res.json({ role: updatedRows[0] })
  } catch (err) {
    console.error('Failed to update role:', err)
    res.status(500).json({ message: 'Server error' })
  }
})

// DELETE /api/roles/:id
router.delete('/roles/:id', authMiddleware, async (req, res) => {
  try {
    const id = parseInt(req.params.id, 10)
    if (isNaN(id)) return res.status(400).json({ message: 'Invalid role id' })

    // prevent deleting a role that is still assigned to users
    const [urows] = await pool.query('SELECT id FROM atb_user WHERE role_id = ?', [id])
    if (urows && urows.length > 0) return res.status(409).json({ message: 'Role is assigned to users' })

    await pool.query('DELETE FROM atb_role WHERE id = ?', [id])

    // log delete
    createLog(req, { message: `删除角色 id=${id}`, tp: 'delete', obj_json: JSON.stringify({ id }) })

    res.json({ ok: true })
  } catch (err) {
    console.error('Failed to delete role:', err)
    res.status(500).json({ message: 'Server error' })
  }
})

// GET /api/roles/:id/menus
// returns { menu_ids: [1,2,3] }
router.get('/roles/:id/menus', authMiddleware, async (req, res) => {
  try {
    const id = parseInt(req.params.id, 10)
    if (isNaN(id)) return res.status(400).json({ message: 'Invalid role id' })

    const [rows] = await pool.query('SELECT menu_id FROM atb_menu_role WHERE role_id = ?', [id])
    const menuIds = (rows || []).map(r => r.menu_id)
    return res.json({ menu_ids: menuIds })
  } catch (err) {
    console.error('Failed to fetch role menus:', err)
    return res.status(500).json({ message: 'Server error' })
  }
})

// PUT /api/roles/:id/menus
// body: { menu_ids: [1,2,3] }
router.put('/roles/:id/menus', authMiddleware, async (req, res) => {
  let conn
  try {
    const id = parseInt(req.params.id, 10)
    if (isNaN(id)) return res.status(400).json({ message: 'Invalid role id' })

    // normalize incoming menu ids
    const menuIds = Array.isArray(req.body && req.body.menu_ids) ? req.body.menu_ids.map(m => Number(m)).filter(n => !Number.isNaN(n)) : []

    // Expand provided menu ids to include all ancestor (parent) menu ids so
    // parent/second-level menus are kept in authorized menu responses even
    // when only leaf/function items were checked in the UI.
    const expandedSet = new Set(menuIds)
    let toFetch = menuIds.slice()
    while (toFetch.length > 0) {
      // fetch parent rows for current batch
      const [rows] = await pool.query('SELECT id, parent_id FROM atb_menu WHERE id IN (?)', [toFetch])
      toFetch = []
      for (const r of rows) {
        const pid = r.parent_id
        if (pid && !expandedSet.has(pid)) {
          expandedSet.add(pid)
          toFetch.push(pid)
        }
      }
    }

    const finalMenuIds = Array.from(expandedSet)

    conn = await pool.getConnection()
    await conn.beginTransaction()

    // remove existing mappings
    await conn.query('DELETE FROM atb_menu_role WHERE role_id = ?', [id])

    if (finalMenuIds.length > 0) {
      const values = finalMenuIds.map(mid => [id, mid])
      // bulk insert
      await conn.query('INSERT INTO atb_menu_role (role_id, menu_id) VALUES ?', [values])
    }

    await conn.commit()
    conn.release()

    // log permission config
    createLog(req, { message: `配置角色权限 id=${id}`, tp: 'perm', obj_json: JSON.stringify({ menu_ids: finalMenuIds }) })

    return res.json({ ok: true })
  } catch (err) {
    if (conn) {
      try { await conn.rollback(); conn.release() } catch (e) {}
    }
    console.error('Failed to update role menus:', err)
    return res.status(500).json({ message: 'Server error' })
  }
})

module.exports = router
